#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
#include <unicode/ustream.h>
#include "Archive.h"
#include "TableEnums.h"
#include "../utils/UnicodeConverter.h"
#include "../mime/data/Address.h"
#include "../utils/Time.h"

#define foreach BOOST_FOREACH

using namespace boost::posix_time;
using namespace boost::gregorian;
using namespace kc_sf;

// -------------------------------------------------------------- //

Archive::Archive( const Message &msg, ActionType action )
: a_message( msg ),
  a_action( action )
{
}

// -------------------------------------------------------------- //

void Archive::operator ()()
{
	UnicodeConverter uconv( "utf8" );

	std::string table_name = ArchiveTableTypeToString.at( ArchiveMessage );

	size_t sender_id = insertEmailAddress( a_message.mRawMessage.rmMailFrom );
	size_t from_email_id = insertEmailAddress( a_message.mFrom );

	DatabaseRecord column_names;
	column_names.push_back( "size" );
	column_names.push_back( "filepath" );
	column_names.push_back( "came_date" );
	column_names.push_back( "sender_id" );
	column_names.push_back( "from_email_id" );
	column_names.push_back( "subject" );
	column_names.push_back( "action" );
	if( !a_message.mBodyParts.empty() )
		column_names.push_back( "body" );

	DatabaseRecord values;
	values.push_back( boost::lexical_cast <std::string>( a_message.mSize ) );
	values.push_back( a_message.mRawMessage.rmFilepath );

	values.push_back( TimeToString()( a_message.mRawMessage.rmCameDate ) );
	values.push_back( boost::lexical_cast <std::string>( sender_id ) );
	values.push_back( boost::lexical_cast <std::string>( from_email_id ) );
	values.push_back( uconv.decode( a_message.mSubject ) );

	values.push_back( boost::lexical_cast <std::string>( a_action ) );
	if( !a_message.mBodyParts.empty() )
		values.push_back( uconv.decode( a_message.getBody() ) );

	size_t msg_id = a_conn.insertInto( table_name, column_names, values );

	typedef std::pair <std::string, Header *> HeaderNamePair;
	foreach( HeaderNamePair header_name, a_message.mHeaders )
	{
		insertHeader( msg_id, *( header_name.second ) );
	}

	foreach ( Attachment *attachment, a_message.mAttachments )
	{
		insertAttachment( msg_id, *attachment );
	}

	insertMessageRecipients( msg_id );
	insertMessageTo( msg_id );
	insertMessageCc( msg_id );
	insertMessageBcc( msg_id );
}

// -------------------------------------------------------------- //

size_t Archive::insertHeader( size_t msg_id, const Header &header )
{
	std::string table_name = ArchiveTableTypeToString.at( ArchiveHeader );

	DatabaseRecord column_names;
	column_names.push_back( "name" );
	column_names.push_back( "value" );
	column_names.push_back( "msg_id" );

	DatabaseRecord values;
	values.push_back( header.getName() );
	values.push_back( UnicodeConverter( "utf8" ).decode( header.getValue() ) );
	values.push_back( boost::lexical_cast <std::string>( msg_id ) );

	return a_conn.insertInto( table_name, column_names, values );
}

// -------------------------------------------------------------- //

size_t Archive::insertAttachment( size_t msg_id, const Attachment &attch )
{
	std::string table_name = ArchiveTableTypeToString.at( ArchiveAttachment );

	DatabaseRecord column_names;
	column_names.push_back( "size" );
	column_names.push_back( "ctype" );
	column_names.push_back( "filename" );
	column_names.push_back( "msg_id" );

	DatabaseRecord values;
	values.push_back( boost::lexical_cast <std::string>( attch.getSize() ) );
	values.push_back( attch.getContentType().toString() );
	values.push_back( attch.getFilename() );
	values.push_back( boost::lexical_cast <std::string>( msg_id ) );

	return a_conn.insertInto( table_name, column_names, values );
}

// -------------------------------------------------------------- //

void Archive::insertMessageRecipients( size_t msg_id )
{
	std::string table_name = ArchiveTableTypeToString.at( ArchiveMessageRecipients );

	DatabaseRecord column_names;
	column_names.push_back( "message_id" );
	column_names.push_back( "email_id" );

	foreach( Address rcpt, a_message.mRawMessage.rmRcptsTo )
	{
		size_t email_id = insertEmailAddress( rcpt );

		DatabaseRecord values;
		values.push_back( boost::lexical_cast <std::string>( msg_id ) );
		values.push_back( boost::lexical_cast <std::string>( email_id ) );

		a_conn.insertInto( table_name, column_names, values );
	}
}

// -------------------------------------------------------------- //

void Archive::insertMessageTo( size_t msg_id )
{
	std::string table_name = ArchiveTableTypeToString.at( ArchiveMessageTo );

	DatabaseRecord column_names;
	column_names.push_back( "message_id" );
	column_names.push_back( "email_id" );

	foreach( Address rcpt, a_message.mTo )
	{
		size_t email_id = insertEmailAddress( rcpt );

		DatabaseRecord values;
		values.push_back( boost::lexical_cast <std::string>( msg_id ) );
		values.push_back( boost::lexical_cast <std::string>( email_id ) );

		a_conn.insertInto( table_name, column_names, values );
	}
}

// -------------------------------------------------------------- //

void Archive::insertMessageCc( size_t msg_id )
{
	std::string table_name = ArchiveTableTypeToString.at( ArchiveMessageCc );

	DatabaseRecord column_names;
	column_names.push_back( "message_id" );
	column_names.push_back( "email_id" );

	foreach( Address rcpt, a_message.mCc )
	{
		size_t email_id = insertEmailAddress( rcpt );

		DatabaseRecord values;
		values.push_back( boost::lexical_cast <std::string>( msg_id ) );
		values.push_back( boost::lexical_cast <std::string>( email_id ) );

		a_conn.insertInto( table_name, column_names, values );
	}
}

// -------------------------------------------------------------- //

void Archive::insertMessageBcc( size_t msg_id )
{
	std::string table_name = ArchiveTableTypeToString.at( ArchiveMessageBcc );

	DatabaseRecord column_names;
	column_names.push_back( "message_id" );
	column_names.push_back( "email_id" );

	foreach( Address rcpt, a_message.mBcc )
	{
		size_t email_id = insertEmailAddress( rcpt );

		DatabaseRecord values;
		values.push_back( boost::lexical_cast <std::string>( msg_id ) );
		values.push_back( boost::lexical_cast <std::string>( email_id ) );

		a_conn.insertInto( table_name, column_names, values );
	}
}

// -------------------------------------------------------------- //

size_t Archive::insertEmailAddress( const Address &email )
{
	std::string table_name = ArchiveTableTypeToString.at( ArchiveEmail );

	DatabaseRecord column_names;
	column_names.push_back( "name" );
	column_names.push_back( "address" );

	DatabaseRecord values;
	values.push_back( email.aDisplayName );
	values.push_back( email.toString() );

	return a_conn.insertInto( table_name, column_names, values );
}
